package cn.iocoder.yudao.module.bpm.controller.admin.taskinfo;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.bpm.controller.admin.projectinfo.vo.BpmProjectInfoSaveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.projectinfo.BpmProjectInfoDO;
import cn.iocoder.yudao.module.bpm.enums.projectinfo.BpmProjectInfoStatusEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.projectinfo.BpmProjectInfoService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;

import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.time.LocalDateTime;
import java.util.*;
import java.io.IOException;
import java.util.function.Function;
import java.util.stream.Collectors;

import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;

import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;

import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

import cn.iocoder.yudao.module.bpm.controller.admin.taskinfo.vo.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.taskinfo.BpmTaskInfoDO;
import cn.iocoder.yudao.module.bpm.service.taskinfo.BpmTaskInfoService;

@Tag(name = "管理后台 - 流程任务信息")
@RestController
@RequestMapping("/bpm/task-info")
@Validated
public class BpmTaskInfoController {

    @Resource
    private BpmTaskInfoService taskInfoService;
    @Resource
    private BpmProcessInstanceService processInstanceService;
    @Resource
    private BpmTaskService taskService;
    @Resource
    private BpmProcessDefinitionService processDefinitionService;
    @Resource
    private BpmCategoryService categoryService;
    @Resource
    private BpmProjectInfoService bpmProjectInfoService;

    @PostMapping("/create")
    @Operation(summary = "创建流程任务信息")
    @PreAuthorize("@ss.hasPermission('bpm:task-info:create')")
    public CommonResult<String> createTaskInfo(@Valid @RequestBody BpmTaskInfoSaveReqVO createReqVO) {
        return success(taskInfoService.createTaskInfo(createReqVO));
    }

    @PutMapping("/update")
    @Operation(summary = "更新流程任务信息")
    @PreAuthorize("@ss.hasPermission('bpm:task-info:update')")
    public CommonResult<Boolean> updateTaskInfo(@Valid @RequestBody BpmTaskInfoSaveReqVO updateReqVO) {
        taskInfoService.updateTaskInfo(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除流程任务信息")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('bpm:task-info:delete')")
    public CommonResult<Boolean> deleteTaskInfo(@RequestParam("id") Long id) {
        taskInfoService.deleteTaskInfo(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得流程任务信息")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('bpm:task-info:query')")
    public CommonResult<BpmTaskInfoRespVO> getTaskInfo(@RequestParam("id") Long id) {
        BpmTaskInfoDO taskInfo = taskInfoService.getTaskInfo(id);
        return success(BeanUtils.toBean(taskInfo, BpmTaskInfoRespVO.class));
    }

    @GetMapping("/statistic")
    @Operation(summary = "获得流程任务统计信息信息")
    @PreAuthenticated
    public CommonResult<BpmTaskInfoStatisticsVO> getTaskInfoStatistic() {
        BpmTaskInfoStatisticsVO taskInfo = taskInfoService.getTaskInfoStatistic();
        return success(taskInfo);
    }

    @GetMapping("/curr/statistic")
    @Operation(summary = "获得流程任务统计信息信息")
    @PreAuthenticated
    public CommonResult<BpmCurrUserTaskInfoStatisticsVO> getCurrUserTaskInfoStatistic() {
        BpmCurrUserTaskInfoStatisticsVO taskInfo = taskInfoService.getCurrUserTaskInfoStatistics();
        return success(taskInfo);
    }

    @GetMapping("/man-hour/statistic")
    @Operation(summary = "获取项目工时统计信息")
    @PreAuthenticated
    public CommonResult<List<BpmProjectTaskManHourStatisticsVO>> getProjectManHourStatistics(){
        List<BpmProjectTaskManHourStatisticsVO> hourStatistics = taskInfoService.getProjectManHourStatistics();
        return success(hourStatistics);
    }

    @GetMapping("/page")
    @Operation(summary = "获得流程任务信息分页")
    @PreAuthorize("@ss.hasPermission('bpm:task-info:query')")
    public CommonResult<PageResult<BpmTaskInfoRespVO>> getTaskInfoPage(@Valid BpmTaskInfoPageReqVO pageReqVO) {
        //1.获取分页任务信息
        Long loginUserId = getLoginUserId();
        pageReqVO.setCreator(loginUserId);
        PageResult<BpmTaskInfoDO> pageResult = taskInfoService.getTaskInfoPage(pageReqVO);

        //2.获取当前页所有任务对应的流程实例id集合
        List<BpmTaskInfoDO> taskInfos = pageResult.getList();
        if (CollectionUtils.isEmpty(taskInfos)){
            return success(BeanUtils.toBean(pageResult, BpmTaskInfoRespVO.class));
        }
        Set<String> procInsIds = taskInfos.stream().map(BpmTaskInfoDO::getProcessInstanceId).collect(Collectors.toSet());

        //3.获取对应的流程实例信息集合
        BpmProcessInstancePageReqVO param = new BpmProcessInstancePageReqVO();
        param.setName(pageReqVO.getProcessName());
        if (!Objects.equals(pageReqVO.getStatus(),BpmProcessInstanceStatusEnum.OVERDUE_FINISHED.getStatus()) &&
                !Objects.equals(pageReqVO.getStatus(),BpmProcessInstanceStatusEnum.OVERDUE.getStatus())){
            param.setStatus(pageReqVO.getStatus());
        }
        param.setProcessDefinitionId(pageReqVO.getProcessDefinitionId());
        param.setCategory(pageReqVO.getCategory());
        param.setStartUserId(getLoginUserId());
        param.setCreateTime(pageReqVO.getCreateTime());
        param.setPageNo(pageReqVO.getPageNo());
        param.setPageSize(pageReqVO.getPageSize());
        //3.1不用根据对当前登录用户的流程，具体展示根据任务来查询对应的流程。权限有任务控制
        List<HistoricProcessInstance> instances = processInstanceService.getAllProcessInstance(null, param, procInsIds);

        if (CollUtil.isEmpty(instances)) {
            return success(PageResult.empty(pageResult.getTotal()));
        }

        // 4.拼接任务节点、流程定义、流程分类信息返回
        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(
                convertList(instances, HistoricProcessInstance::getId));
        Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap(
                convertSet(instances, HistoricProcessInstance::getProcessDefinitionId));
        Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
                convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));
        List<BpmProcessInstanceRespVO> processInstancePages = BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(instances,
                processDefinitionMap, categoryMap, taskMap, null, null);
        //5.在拼接上自建任务信息
        PageResult<BpmTaskInfoRespVO> pageRespResult = BeanUtils.toBean(pageResult, BpmTaskInfoRespVO.class);
        List<BpmTaskInfoRespVO> list = pageRespResult.getList();
        if (CollectionUtils.isEmpty(list) || CollectionUtils.isEmpty(processInstancePages)){
            return success(pageRespResult);
        }
        Map<String, BpmProcessInstanceRespVO> maps = processInstancePages.stream().collect(Collectors.toMap(BpmProcessInstanceRespVO::getId,
                Function.identity(), (key1, key2) -> key2));
        //6.获取项目信息
        List<BpmProjectInfoDO> projectInfos = bpmProjectInfoService.getProjectInfos();
        Map<Long, BpmProjectInfoDO> projectMap = projectInfos.stream().collect(Collectors.toMap(BpmProjectInfoDO::getId,
                Function.identity(), (k1, k2) -> k2));
        for (BpmTaskInfoRespVO vo : list){
            //将任务的开始结束时间设置到任务的字段中，将startTime,endTime保留为流程实例的开始结束时间
            vo.setTaskStartTime(vo.getStartTime());
            vo.setTaskEndTime(vo.getEndTime());
            String processInstanceId = vo.getProcessInstanceId();
            BpmProcessInstanceRespVO bpmProcessInstanceRespVO = maps.get(processInstanceId);
            if (Objects.nonNull(bpmProcessInstanceRespVO)){
                BpmTaskInfoRespVO.builder(vo,bpmProcessInstanceRespVO);
            }
            //设置项目信息
            BpmProjectInfoDO projectInfo = projectMap.get(vo.getProjectId());
            if (Objects.nonNull(projectInfo)){
                vo.setProjectName(projectInfo.getName());
            }
            //判断当前任务是否逾期
            LocalDateTime taskEndTime = vo.getTaskEndTime();
            if (taskEndTime != null && taskEndTime.isBefore(LocalDateTime.now())){
                //更新项目信息的参数
                BpmProjectInfoSaveReqVO projectVo = new BpmProjectInfoSaveReqVO();
                projectVo.setId(vo.getProjectId());
                if (Objects.equals(BpmProcessInstanceStatusEnum.APPROVE.getStatus(), vo.getStatus())){
                    //流程审批状态为审批通过，则修改状态为逾期已完成
                    taskInfoService.updateTaskInfoStatus(vo.getId(),BpmProcessInstanceStatusEnum.OVERDUE_FINISHED.getStatus());
                    vo.setStatus(BpmProcessInstanceStatusEnum.OVERDUE_FINISHED.getStatus());
                    BpmTaskInfoCheckStatusVO taskStatus = taskInfoService.checkTaskStatusInfo(vo.getProjectId(),loginUserId);
                    if (Boolean.TRUE.equals(taskStatus.getIsFinished())){
                        projectVo.setStatus(BpmProjectInfoStatusEnum.FINISHED.getStatus());
                        bpmProjectInfoService.updateProjectInfo(projectVo);
                    }
                } else if (!Objects.equals(BpmProcessInstanceStatusEnum.OVERDUE.getStatus(), vo.getStatus()) &&
                        !BpmProcessInstanceStatusEnum.OVERDUE_FINISHED.getStatus().equals(vo.getStatus())){
                    //流程审批状态不为逾期未完成或逾期已完成，则修改状态为逾期未完成
                    taskInfoService.updateTaskInfoStatus(vo.getId(),BpmProcessInstanceStatusEnum.OVERDUE.getStatus());
                    vo.setStatus(BpmProcessInstanceStatusEnum.OVERDUE.getStatus());
                    //同时修改项目状态为已逾期
                    projectVo.setStatus(BpmProjectInfoStatusEnum.OVERDUE.getStatus());
                    bpmProjectInfoService.updateProjectInfo(projectVo);
                }
            }
        }
        return success(pageRespResult);
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出流程任务信息 Excel")
    @PreAuthorize("@ss.hasPermission('bpm:task-info:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportTaskInfoExcel(@Valid BpmTaskInfoPageReqVO pageReqVO,
              HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<BpmTaskInfoDO> list = taskInfoService.getTaskInfoPage(pageReqVO).getList();
        // 导出 Excel
        ExcelUtils.write(response, "流程任务信息.xls", "数据", BpmTaskInfoRespVO.class,
                        BeanUtils.toBean(list, BpmTaskInfoRespVO.class));
    }



}